import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:stream_data_reader/stream_data_reader.dart';

/// 控制服务端口
const _kControlPort = 10087;

/// 心跳报文超时时间
/// 目前如果超过 15 秒未收到心跳报文回复，则会认定心跳超时，立即中断连接
const _kControlHeartbeatTimeout = 15000;

/// 设备控制服务接口
/// Author CimZzz
/// Since Elec-Communication-Protocol Alpha on 2020/7/14 下午2:53
mixin DeviceControlInterface {
	/// 已经完成连接
	/// isNeedBound 表示是否需要绑定设备
	void onConnected(bool isNeedBound);

	/// 握手验证被拒绝
	void onRefused();

	/// 连接异常断开
	void onError(dynamic e);
}


/// 控制消息结果
/// Author CimZzz
/// Since Elec-Communication-Protocol Alpha
class ControlResult {
    const ControlResult({this.isSuccess, this.message, dynamic data}): _data = data;
    
    /// 执行结果是否成功
	final bool isSuccess;
	
	/// 执行结果返回的消息
	final String message;
	
	/// 执行结果数据
	final Map _data;

    /// 获取事件参数
    T getEventParam<T>(String key, {T defaultValue}) {
	    final data = _data != null ? _data[key] : null;
	    if(data is T) {
		    return data;
	    }
	    return defaultValue;
    }

    @override
    String toString() {
	    return 'ControlResult(isSuccess: $isSuccess, data: $_data, message: $message)';
    }
}

/// 控制信息
/// Author CimZzz
/// Since Elec-Communication-Protocol Alpha on 2020/7/14 上午11:15
class _ControlMessage {
	_ControlMessage({this.eventType, this.eventIdx, this.eventParams});
	
	/// 握手请求
	static const Type_HandShake_Req = 0;
	/// 握手回复
	static const Type_HandShake_Res = 1;
	/// 心跳
	static const Type_Heart_Beat = 2;
	/// 账号绑定
	static const Type_Bind_Account = 3;
	/// 账号绑定回复
	static const Type_Bind_Account_Reply = 4;
	// 连接关闭消息回复
	static const Type_Closed_Message = 5;
	
	/// 事件回执
	static const Type_Event_Reply = 100;
	/// 设置定时开关机
	/// * 已废弃
	static const Type_Alarm = 102;
	/// 播放计划
	static const Type_Schedule = 103;
	/// 删除计划
	static const Type_Del_Schedule = 104;
	/// 排序计划
	/// * 已废弃
	static const Type_Sort_Schedule = 105;
	/// 重置出厂设置计划
	static const Type_Factory_Reset = 106;
	/// 配置音量
	static const Type_Volume = 107;
	/// 配置设备名
	static const Type_Device_Name = 108;
	/// 配置Wifi
	static const Type_Config_Wifi = 109;
	/// 配置直播插播计划
	static const Type_Inter_Live = 110;
	/// 配置网页插播计划
	static const Type_Inter_Web = 111;
	/// 停止插播计划
	static const Type_Close_Inter = 112;
	/// 解绑设备
	static const Type_Unbound_Device = 113;
	/// 升级设备
	static const Type_Upgrade_Device = 114;
	/// 取消升级设备
	static const Type_Cancel_Upgrade_Device = 115;
	/// 播放计划
	static const Type_Play_Schedule = 116;
	/// 停播计划
	static const Type_Stop_Schedule = 117;
	/// 停止计划下载
	static const Type_Cancel_DownloadSchedule = 121;
	
	/// 事件类型
	final int eventType;
	
	/// 事件下标
	final int eventIdx;
	
	/// 事件参数
	final Map eventParams;
	
	/// 获取事件参数
	T getEventParam<T>(String key, {T defaultValue}) {
		final data = eventParams != null ? eventParams[key] : null;
		if(data is T) {
			return data;
		}
		return defaultValue;
	}
	
	@override
	String toString() {
		return '_ControlMessage(eventType = $eventType, eventIdx: $eventIdx, eventParams: $eventParams)';
	}
}


/// 控制信息 Writer
/// Author CimZzz
/// Since Elec-Communication-Protocol Alpha
class __ControlMessageWriter {
	__ControlMessageWriter(this.socket);
	
	final Socket socket;
	
	void writeMessage(_ControlMessage message) {
		final buffer = _bytes(message);
		final size = buffer.length;
		socket.add([(size & 0xFF), ((size >> 8) & 0xFF)]);
		socket.add(buffer);
	}
	
	List<int> _bytes(_ControlMessage message) {
		final map = <String, dynamic>{};
		map['eventType'] = message.eventType;
		map['eventIdx'] = message.eventIdx;
		map['eventParams'] = message.eventParams;
		return utf8.encode(json.encode(map));
	}
}

/// 设备控制服务
/// Author CimZzz
/// Since Elec-Communication-Protocol Alpha
class DeviceControlService {
	DeviceControlService(this._accountId, this._interface):
		assert(_interface != null),
		assert(_accountId != null);

	/// 账号 Id
	final String _accountId;

	/// 设备控制回调接口
	final DeviceControlInterface _interface;

	/// 连接套接字
	Socket _socket;

	/// 控制流订阅器
	StreamSubscription<_ControlMessage> _streamSubscription;

	/// 控制信息 Writer
	__ControlMessageWriter _writer;

	/// 心跳计时器
	Timer _heartbeatTimer;

	/// 心跳等待时间
	int _heartbeatWaitTime;

	/// 是否成功连接
	bool _isConnected;

	/// 检查服务是否成功启动
	bool get _checkServiceStarted => _socket != null;

	/// 绑定设备 Completer
	Completer<ControlResult> _bindAccountIdCompleter;

	/// 控制指令 Completer
	Map<int, dynamic> _controlCompleterMap;

	/// 控制指令 Completer 自增下标
	var _controlCompleterIdx = 0;

	/// 判断服务是否已经关闭
	var _isClosed = false;

	/// 启动设备控制服务
	Future<void> start(String ip, {bool isTest}) async {
		if(!_checkServiceStarted) {
			_socket = await Socket.connect(ip, _kControlPort);
			if(_isClosed) {
				_socket?.destroy();
				_socket = null;
				return;
			}

			_writer = __ControlMessageWriter(_socket);
			_streamSubscription = transformByteStream(_socket, (dataReader) async {
				try {
					final sizeOne = await dataReader.readOneByte();
					final sizeTwo = await dataReader.readOneByte();
					final size = (sizeOne & 0xFF) | ((sizeTwo & 0xFF) << 8);
					final buffer = await dataReader.readBytes(length: size);
					final map = json.decode(utf8.decode(buffer));
					final eventType = map['eventType'];
					final eventIdx = map['eventIdx'];
					final eventParams = map['eventParams'];
					if(eventType == null) {
						return null;
					}
					if(eventType is int && (eventParams == null || eventParams is Map)) {
						return _ControlMessage(eventType: eventType, eventIdx: eventIdx, eventParams: eventParams);
					}
				}
				catch(e) {
					// 发生异常，直接终止
					_interface?.onError(e);
					close();
				}
				return null;
			}).listen((event) {
				if(event != null) {
					_handleControlMessage(event);
				}
			}, onError: (e) {
				_interface?.onError(e);
				close();
			}, onDone: () {
				close();
			});
			_heartbeatWaitTime = DateTime.now().millisecondsSinceEpoch;
			_heartbeatTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
				_checkHeartbeat();
			});
			// 发送握手信息
			_sendMessage(_ControlMessage(
				eventType: _ControlMessage.Type_HandShake_Req,
				eventParams: {
					'accountId': _accountId,
					'isTest': isTest
				}));
		}
	}

	/// 关闭设备控制服务
	void close() {
		_isClosed = true;
		_isConnected = false;

		_streamSubscription = null;
		
		_heartbeatTimer?.cancel();
		_heartbeatTimer = null;
		
		_socket?.destroy();
		_socket = null;
		
		_writer = null;
		
		// 完成待完成的 Completer
		_completeControlResult(_bindAccountIdCompleter, isSuccess: false);
		_bindAccountIdCompleter = null;
		
		_controlCompleterIdx = 0;
		_controlCompleterMap?.forEach((key, value) {
			_completeControlResult(value, completedResult: const ControlResult(isSuccess: false, message: '连接已断开'));
		});
		_controlCompleterMap = null;
	}
	
	
	///
	/// 外部发送消息接口
	///
	
	/// 发送指令绑定账号
	Future<ControlResult> bindAccountId() async {
		if(_bindAccountIdCompleter != null) {
			return _bindAccountIdCompleter.future;
		}
		
		return await _wrapperResult(() {
			_bindAccountIdCompleter = Completer();
			// 发送绑定账户指令
			_sendMessage(_ControlMessage(
				eventType: _ControlMessage.Type_Bind_Account,
			));
			return _bindAccountIdCompleter;
		});
	}

	/// 发送推送计划指令
	Stream<ControlResult> pushSchedule(String scheduleId, bool isPlay) {
		return _wrapperStreamResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Schedule,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'scheduleId': scheduleId,
					'isPlay': isPlay
				}
			));
			return _autoCreateStream();
		});
	}

	/// 发送删除计划指令
	Future<ControlResult> deleteSchedule(String scheduleId) async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Del_Schedule,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'scheduleId': scheduleId
				}
			));
			return _autoCreateCompleter();
		});
	}

	/// 发送排序计划指令
	/// * 已废弃
	Future<ControlResult> sortSchedule(String sortIdx) async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Sort_Schedule,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'sortIdx': sortIdx
				}
			));
			return _autoCreateCompleter();
		});
	}

	/// 发送恢复出厂设置指令
	Future<ControlResult> factoryReset() async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Factory_Reset,
				eventIdx: _controlCompleterIdx,
			));
			return _autoCreateCompleter();
		});
	}

	/// 发送配置音量指令
	/// 音量值为音量百分比，可为负值，总音量取值范围为 0 ~ 1
	Future<ControlResult> configVolume(double volume) async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Volume,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'volume': volume
				}
			));
			return _autoCreateCompleter();
		});
	}

	/// 发送配置设备名指令
	Future<ControlResult> configDeviceName(String deviceName) async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Device_Name,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'deviceName': deviceName
				}
			));
			return _autoCreateCompleter();
		});
	}

	/// 发送配置设备WIFI指令
	Future<ControlResult> configDeviceWifi(String ssid, String password, String capabilities) async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Config_Wifi,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'ssid': ssid,
					'password': password,
					'capabilities': capabilities,
				}
			));
			return _autoCreateCompleter();
		});
	}

	/// 发送直播插播计划
	Future<ControlResult> configInterLive(String livePath) async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Inter_Live,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'livePath': livePath,
				}
			));
			return _autoCreateCompleter();
		});
	}

	/// 发送网页插播计划
	Future<ControlResult> configInterWeb(String webPath) async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Inter_Web,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'webPath': webPath,
				}
			));
			return _autoCreateCompleter();
		});
	}

	/// 发送停止插播计划
	Future<ControlResult> closeInterSchedule() async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Close_Inter,
				eventIdx: _controlCompleterIdx,
			));
			return _autoCreateCompleter();
		});
	}
	
	/// 发送解绑设备
	Future<ControlResult> unboundDevice() async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Unbound_Device,
				eventIdx: _controlCompleterIdx,
			));
			return _autoCreateCompleter();
		});
	}
	
	/// 发送升级设备
	Stream<ControlResult> upgradeDevice(String downloadUrl, String fileMd5, {bool isFromInner}) {
		return _wrapperStreamResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Upgrade_Device,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'downloadUrl': downloadUrl,
					'fileMd5': fileMd5,
					'isFromInner': isFromInner,
				}
			));
			return _autoCreateStream();
		});
	}
	
	/// 取消升级设备
	Future<ControlResult> cancelUpgradeDevice() async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Cancel_Upgrade_Device,
				eventIdx: _controlCompleterIdx,
			));
			return _autoCreateCompleter();
		});
	}

	/// 开始播放计划
	Future<ControlResult> playSchedule(String scheduleId) async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Play_Schedule,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'scheduleId': scheduleId
				}
			));
			return _autoCreateCompleter();
		});
	}

	/// 停止播放计划
	Future<ControlResult> stopSchedule(String scheduleId) async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Stop_Schedule,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'scheduleId': scheduleId
				}
			));
			return _autoCreateCompleter();
		});
	}


	/// 停止计划下载
	Future<ControlResult> cancelScheduleDownload(String scheduleId) async {
		return await _wrapperResult(() {
			_sendMessage(_ControlMessage (
				eventType: _ControlMessage.Type_Cancel_DownloadSchedule,
				eventIdx: _controlCompleterIdx,
				eventParams: {
					'scheduleId': scheduleId
				}
			));
			return _autoCreateCompleter();
		});
	}


	///
	/// 内部处理方法
	///
	
	/// 自动生成控制指令 Completer
	Completer<ControlResult> _autoCreateCompleter() {
		_controlCompleterMap ??= {};
		final completer = Completer<ControlResult>();
		_controlCompleterMap[_controlCompleterIdx] = completer;
		_controlCompleterIdx ++;
		return completer;
	}
	
	/// 自动生成控制指令 StreamController
	StreamController<ControlResult> _autoCreateStream() {
		_controlCompleterMap ??= {};
		final completer = StreamController<ControlResult>();
		_controlCompleterMap[_controlCompleterIdx] = completer;
		_controlCompleterIdx ++;
		return completer;
	}
	
	/// 控制指令包装器
	/// 用来限制在建立连接之前的控制消息发送
	FutureOr<ControlResult> _wrapperResult(Completer<ControlResult> Function() completerBuilder) {
		if(!_isConnected) {
			return ControlResult(isSuccess: false, message: '尚未完成握手连接');
		}
		final completer = completerBuilder();
		return completer.future;
	}
	
	/// 控制指令包装器
	/// 用来限制在建立连接之前的控制消息发送
	Stream<ControlResult> _wrapperStreamResult(StreamController<ControlResult> Function() completerBuilder) {
		if(!_isConnected) {
			return Stream.value(ControlResult(isSuccess: false, message: '尚未完成握手连接'));
		}
		final completer = completerBuilder();
		return completer.stream;
	}
	
	/// 发送消息
	void _sendMessage(_ControlMessage message) {
		if(_checkServiceStarted) {
			try {
				_writer?.writeMessage(message);
			}
			catch(e) {
				close();
			}
		}
	}
	
	/// 处理控制信息
	void _handleControlMessage(_ControlMessage controlMessage) {
		// 刷新心跳等待时间
		_heartbeatWaitTime = DateTime.now().millisecondsSinceEpoch;
		switch(controlMessage.eventType) {
			case _ControlMessage.Type_HandShake_Res:
			// 收到握手回复，查看建立连接结果
				var isSuccess = controlMessage.getEventParam('isSuccess', defaultValue: false);
				var isNeedBound = controlMessage.getEventParam('isNeedBound', defaultValue: false);
				if(isSuccess) {
					_isConnected = true;
					_interface?.onConnected(isNeedBound);
					if(!isNeedBound) {
						_bindAccountIdCompleter ??= Completer();
						_completeControlResult(_bindAccountIdCompleter, isSuccess: true);
					}
				}
				else {
					_interface?.onRefused();
					close();
				}
				break;
			case _ControlMessage.Type_Closed_Message:
			// 收到断开连接前消息
				var message = controlMessage.getEventParam('message');
				_interface?.onError(message ?? '连接已断开');
				close();
				break;
			case _ControlMessage.Type_Heart_Beat:
			// 收到心跳报文时转发心跳报文
				_sendMessage(controlMessage);
				break;
				
			case _ControlMessage.Type_Bind_Account_Reply:
			// 收到绑定账户回复
				_bindAccountIdCompleter ??= Completer();
				_completeControlResult(_bindAccountIdCompleter, isSuccess: true);
				break;
			case _ControlMessage.Type_Event_Reply:
				final idx = controlMessage.eventIdx;
				if(idx != null) {
					final isPending = controlMessage.getEventParam('isPending', defaultValue: false);
					dynamic completer;
					if(isPending) {
						completer = _controlCompleterMap != null ? _controlCompleterMap[idx] : null;
					}
					else {
						completer = _controlCompleterMap?.remove(idx);
					}
					
					if(completer != null) {
						_completeControlResult(
							completer,
							isClosed: isPending != true,
							isSuccess: controlMessage.getEventParam('isSuccess', defaultValue: false),
							message: controlMessage.getEventParam('message', defaultValue: null),
							data: controlMessage.eventParams
						);
						
					}
				}
				break;
		}
	}
	
	/// 检查心跳
	void _checkHeartbeat() {
		if(DateTime.now().millisecondsSinceEpoch - _heartbeatWaitTime >= _kControlHeartbeatTimeout) {
			_interface?.onError('心跳检测超时');
			close();
		}
	}
	
	/// 完成控制消息结果
	void _completeControlResult(dynamic completer, {bool isSuccess = true, bool isClosed = false, String message, dynamic data, ControlResult completedResult}) {
		if(completer is Completer<ControlResult>) {
			if(!completer.isCompleted) {
				completer.complete(completedResult ?? ControlResult(isSuccess: isSuccess, message: message, data: data));
			}
		}
		else if(completer is StreamController<ControlResult>) {
			completer.add(completedResult ?? ControlResult(isSuccess: isSuccess, message: message, data: data));
			if(isClosed == true) {
				completer.close();
			}
		}
	}
}